/*
 * @(#)TerminatorImpl.java	1.0 2000/12/14
 *
 * Copyright 2000 Thomas Barnekow. All Rights Reserved.
 *
 * This software is the proprietary information of Thomas Barnekow.
 * Use is subject to license terms.
 */

package tb.jots.impl;

import java.util.*;
import tb.jots.*;

/**
 * {@link Terminator} implementation.
 *
 * @author Thomas Barnekow
 * @version 1.0
 */
public class TerminatorImpl implements Terminator {

  /**
   * {@link Control} object.
   */
  ControlImpl m_control;

  /**
   * Don't let everybody create instances.
   */
	TerminatorImpl(ControlImpl control) {
    m_control = control;
  }

	public synchronized void commit(boolean reportHeuristics) throws JotsException {
    checkCallingThread();

		// Check status and perform rollback if necessary
		if (getStatus() == OTS.STATUS_MARKED_ROLLBACK) {
			_rollback();
			throw new TransactionRolledBackException();
		}

		// Phase 1: Let'em vote
		setStatus(OTS.STATUS_PREPARING);
		List phaseTwoResources = new Vector();

		Iterator iterator = resources();
		while (iterator.hasNext()) {
			Resource resource = (Resource) iterator.next();

			int vote = resource.prepare();
			if (vote == OTS.VOTE_ROLLBACK) {
				setStatus(OTS.STATUS_MARKED_ROLLBACK);
				_rollback();
				throw new TransactionRolledBackException();
			} else if (vote == OTS.VOTE_COMMIT) {
				phaseTwoResources.add(resource);
			}
		}

		setStatus(OTS.STATUS_PREPARED);

		// Phase 2: Commit changes
		setStatus(OTS.STATUS_COMMITTING);

		HeuristicRollbackException heuristicRollback = null;
		HeuristicMixedException heuristicMixed = null;
		HeuristicHazardException heuristicHazard = null;

		try {
			iterator = phaseTwoResources.iterator();
			while (iterator.hasNext()) {
				Resource resource = (Resource) iterator.next();
				try {
          // Revisit: should we switch to rollback if an exception had been thrown?
					resource.commit();
				} catch (NotPreparedException ex) {
					// Can't happen here
				} catch (HeuristicRollbackException ex) {
					heuristicRollback = ex;
					resource.forget();
				} catch (HeuristicMixedException ex) {
					heuristicMixed = ex;
					resource.forget();
				} catch (HeuristicHazardException ex) {
					heuristicHazard = ex;
					resource.forget();
				} catch (Exception ex) {
					ex.printStackTrace(System.err);
					resource.forget();
				}
			}

      // Check heuristic outcome
      if (reportHeuristics) {
        if (heuristicHazard != null) {
          setStatus(OTS.STATUS_UNKNOWN);
          throw heuristicHazard;
        }
        if (heuristicMixed != null) {
          setStatus(OTS.STATUS_UNKNOWN);
          throw heuristicMixed;
        }
        if (heuristicRollback != null) {
          setStatus(OTS.STATUS_UNKNOWN);
          throw new HeuristicMixedException(
            "At least one Resource threw a HeuristicRollbackException",
            heuristicRollback);
        }
      }

      // Successfully done.
      setStatus(OTS.STATUS_COMMITTED);

		} finally {
			resetTransactionContext();
		}
	}

	public synchronized void rollback() throws JotsException	{
    checkCallingThread();
    _rollback();
  }

	void _rollback() throws JotsException	{
		HeuristicCommitException heuristicCommit = null;
		HeuristicMixedException heuristicMixed = null;
		HeuristicHazardException heuristicHazard = null;

		try {
			setStatus(OTS.STATUS_ROLLING_BACK);
			Iterator iterator = resources();
			while (iterator.hasNext()) {
				Resource resource = (Resource) iterator.next();
				try {
					resource.rollback();
				} catch (HeuristicCommitException ex) {
					heuristicCommit = ex;
					resource.forget();
				} catch (HeuristicMixedException ex) {
					heuristicMixed = ex;
					resource.forget();
				} catch (HeuristicHazardException ex) {
					heuristicHazard = ex;
					resource.forget();
				} catch (Exception ex) {
					ex.printStackTrace(System.err);
					resource.forget();
				}
			}

      // Throw any heuristic exception
      if (heuristicHazard != null) {
        setStatus(OTS.STATUS_UNKNOWN);
        throw heuristicHazard;
      }
      if (heuristicMixed != null) {
        setStatus(OTS.STATUS_UNKNOWN);
        throw heuristicMixed;
      }
      if (heuristicCommit != null) {
        setStatus(OTS.STATUS_UNKNOWN);
        throw new HeuristicMixedException(
          "At least one Resource threw a HeuristicCommitException",
          heuristicCommit);
      }

      // Successfully done.
      setStatus(OTS.STATUS_ROLLED_BACK);

		} finally {
			resetTransactionContext();
		}
	}

  /*
   * Implementation-specific operations
   */

  /**
   * Get {@link Coordinator} implementation object.
   *
   * @exception UnavailableException
   */
	CoordinatorImpl getCoordinator() throws JotsException {
    if (m_control == null) {
      throw new UnavailableException();
    }
    return (CoordinatorImpl) m_control.getCoordinator();
  }

  void setStatus(int status) throws JotsException {
    getCoordinator().setStatus(status);
	}

  int getStatus() throws JotsException {
    return getCoordinator().getStatus();
  }

  Iterator resources() throws JotsException {
    return getCoordinator().resources();
  }

	void resetTransactionContext() {
    if (m_control != null) {
      // Reset Coordinator context
      try {
        getCoordinator().resetTransactionContext();
      } catch (JotsException ex) {}

      // Reset Control context
      m_control.resetTransactionContext();
    }

    // Reset (own) Terminator context
    m_control = null;
	}

  void checkCallingThread() throws NoPermissionException {
    if (m_control == null) {
      throw new NoPermissionException();
    }
    m_control.checkCallingThread();
  }
}
